
//#include "rendertexture.h"

#include <windows.h>
#include <time.h>
#include <string.h>

#include "glew.h"
#include "wglew.h"
#include "glut.h"

#include "linalg.h"

#include "cg/cg.h"
#include "cg/cgGL.h"


#include "OBJHandler.h"
#include "TextureHandler.h"
#include "ShaderHandler.h"
#include "RenderTextureFBO.h"
#include <floatimage.h>

#pragma comment(lib,"cg.lib");
#pragma comment(lib,"cggl.lib");
#pragma comment(lib,"glew32.lib");

int isDrawLine = 0;

int* displayListID;
void drawModel();
//void drawSky();
void reloadShaders();
int* textureID;
int totalFrame;
ShaderHandler fShader, vShader;
int cgTextureIDAlbedo;
int cgTextureIDNormalR;
int cgTextureIDNormalG;
int cgTextureIDNormalB;
ShaderHandler skyFShader, skyVShader;

int cgEnvID, cgEnvDiffID, cgEnvSpec1ID, cgEnvSpec2ID;

GLfloat ObjectToLightMatrix[16];
GLfloat ProjectiveMatrix[16];

RenderTexture *rtShadow = 0;
RenderTexture *rtIrradiance = 0;
RenderTexture *rtPosition = 0;
RenderTexture *rtGaussianPass[6] = {0};
RenderTexture *rtTemp = 0;

ShaderHandler irradianceFShader, irradianceVShader;
ShaderHandler positionFShader, positionVShader;
ShaderHandler vblurFShader;
ShaderHandler ublurFShader;
ShaderHandler gaussianSkinFShader, gaussianSkinVShader;

int cgTextureIDPosition;
int cgTextureIDIrradiance;
int cgTextureIDShadowMap;
float cgFloatGaussianWidth;
int cgTextureIDGaussian[6];

ShaderHandler depthFShader, depthVShader;
unsigned int mTexDepth, mFBODepth;

ShaderHandler aoFShader, aoVShader;
ShaderHandler compositeFShader, compositeVShader;

int cgTextureIDScene;

float farPlane = 70;
float nearPlane = 130;

bool ifPrintScreen = false;

bool isNoVerticalLightMovement = false;

#define numFrame 134


//Size of shadow map
const int shadowMapSize=2800;

//Textures
GLuint shadowMapTexture;
GLuint projTexture;

//mat4f textureMatrix;
float* textureMatrix_array;


//Camera camera;
float m_aspect;
float m_frustum_y;

vec3f L;
vec3f V;

vec3f T;
vec3f Rx, Ry, Rz;
float fc, pu, pv;
float w_image, h_image;


mat3f R_trackball[3]; //camera, object, light
mat3f R_temp[3];	
vec3f v1;
vec3f v2;
vec3f axis;


vec3f pos;
float grey = 0;
bool isSetDefaultCamera = true;

mat3f R1, R2, R3;

float trans;
bool play = false;


class TrackBall {
public:
	TrackBall(){ R = R.Identity(); 	R_temp = R_temp.Identity();};
	~TrackBall(){};

	void updateV1(int x, int y, int W, int H){//, vec3f& vec){
		v1 = calVec(x, y, W, H);
		//vec = v1;
	}
	void updateV2(int x, int y, int W, int H){//, vec3f& vec, mat3f& rot){
		v2 = calVec(x, y, W, H);
		axis = v1^v2;
		axis.Normalize();
		//axis = vec3f(0,1,0);
		float angle = acos(v1*v2);
		if(v1!=v2){
			R_temp =  R.RotationFromAxisAngle(axis, angle*3.0);
			//R = R_temp * R;
		}
		v1 = v2;
	}
	mat3f R;
	mat3f R_temp;
private:
	vec3f calVec(int x, int y, int W, int H){
		// record v1
		float r = sqrtf(W*W/4.0+H*H/4.0);
		float px = x - W/2.0;
		float py = H/2.0 - y;
		px /= r;
		py /= r;
		float pz = (1-px*px-py*py);
		if(pz<0){ 
			pz = 0;
			px = px / sqrtf(px*px+py*py);
			py = py / sqrtf(px*px+py*py);
		}else
			pz = sqrtf(pz);
		vec3f v = vec3f(px, py, pz);



		v.Normalize();
		return v;
	}
	vec3f v1;
	vec3f v2;
	vec3f axis;


};

TrackBall trackballs[3];
bool tracking = false;
bool ifPrintImages = false;

//int win_h, win_w;
int target;
float x_camera = 0;
float y_camera = 0;
float z_camera = 0;
float mouseBeginX=0;
float mouseBeginY=0;
int mouseMotion = 0;
bool ifZoom = 0;
bool ifPrintHelp = false;
int R_item_id;
int G_item_id;
int B_item_id;
int S_item_id;
int diff_id = 5;
int spec_id = 6;
int sm_id = 7;
int disp_debug_id = 8;
int disp_neutral_id = 9;
int leftEyeInner = 12;
int rightEyeInner = 13;
int upperTeeth = 14;
int lowerTeeth = 15;
int tongue = 16;
int toplid = 17;
int lowerlid = 18;
int logo = 19;
int blur = 20;
int cubemap = 21;

GLuint cubeid;
float weight[4];

int disp_id[30];
int num_polygon;
bool print = false;

int view_from = 2;

float frame_value[30];


ULONG lastTick=GetTickCount(),currTick=GetTickCount();
ULONG UPDATESPEED = 30;
UINT frames=0;
float fps=60.0f;
float g_speed;

//WMp3 *mp3;


// TIME
LARGE_INTEGER pframe, cframe;

LARGE_INTEGER init_time;

LARGE_INTEGER aTime, bTime, cTime;

float pass_time;

//manip_scene		*g_scene;


int				g_window_id;
int				g_camera_mode=0;
int				g_camera_track=0;
int				g_camera_replay=0;
int				g_replay_frame=0;
char			*g_camera_mode_string[]		= {"Camera", "Lamp"};

mat4f			g_lightview_projection;

float			spec_intensity_01;
float			spec_power_01;
float			spec_intensity_02;
float			spec_power_02;
float			diff_r, diff_g, diff_b, spec;



#define			FLAG_RELOAD 302
#define			FLAG_TRACK  303
#define			FLAG_REPLAY	304
#define			FLAG_LOAD_TRACK	305
#define			FLAG_DEFAULT_CAMERA 307
#define			FLAG_SHADING 306
#define			FLAG_BLEND 308

float frame = 0;
int frame_int = 0;

int isWireframe = false;
int shading_option;
int win_w = 1024;//1500;
int win_h = 768;//1000;

int isSkybox;
int isGlow;


float skin_mode= 9;



CGcontext context;
CGprofile cg_vprofile, cg_fprofile;




///////////

ULONG global_time;
ULONG init_global_time;
ULONG audio_time;
bool first_time = true;

ULONG start_time;
ULONG end_time;


unsigned char	*output_buffer = NULL;

void wglGetLastError();
void DrawAxes(float scale);




void CheckCgError(void)
{
	CGerror err = cgGetError();

	if (err != CG_NO_ERROR)
	{
		printf("CG error: %s\n", cgGetErrorString(err));
		_exit(1);
	}
}
void setDefaultCamera(){

	float m = h_image/(2.0*fc);//0.064914786884156672535796152951729;
	float angle = 2*atan(m);
	m_frustum_y = angle * 180.0/3.1415;
	m_frustum_y = 20;
	printf("focal length: %f\n", 768/2.0/tan(10.0*3.1415926/180.0));
	//getchar();
	//printf("frums %f\n", m_frustum_y);
	//Rcw
	mat4f Rcw = mat4f( Rx.x, Rx.y, Rx.z, T.x,
		Ry.x, Ry.y, Ry.z, T.y,
		Rz.x, Rz.y, Rz.z, T.z,
		0,	 0,    0,    1); 
	mat4f Rwc = Rcw.Inverse();

	trackballs[0].R = Rwc.GetRotationMatrix();
	pos = Rwc.GetTranslationVector();
	printf("pos %f %f %f ", pos.x, pos.y, pos.z);




	trackballs[0].R = trackballs[0].R.Identity();
	trackballs[1].R = trackballs[1].R.Identity();
	trackballs[2].R = trackballs[2].R.Identity();
	x_camera = y_camera = 0.0;
	z_camera = -100;

	//x_camera = 0.1;y_camera = -1.40;z_camera = 0.00;
	R_trackball[0] = R_trackball[0].Identity();
	R_trackball[1] = R_trackball[1].Identity();
	R_trackball[2] = R_trackball[2].Identity();
	R_temp[0] = R_temp[0].Identity();
	R_temp[1] = R_temp[1].Identity();
	R_temp[2] = R_temp[2].Identity();


}

void cgErrorCallback(void)
{
	CGerror lastError = cgGetError();
	if(lastError) {
		const char *listing = cgGetLastListing(context);
		printf("%s\n", cgGetErrorString(lastError));
		printf("%s\n", listing);
		exit(-1);
	}
}


void initCg()
{
	cgSetErrorCallback(cgErrorCallback);
	context = cgCreateContext();

	cg_vprofile = cgGLGetLatestProfile(CG_GL_VERTEX);
	cg_fprofile = cgGLGetLatestProfile(CG_GL_FRAGMENT);


}
void createBuffers()
{
	//RenderTexture *rtShadow = 0;
	//RenderTexture *rtIrradiance = 0;
	//RenderTexture *rtPosition = 0;
	//RenderTexture *rtGaussianPass[6] = {0};
	//RenderTexture *rtTemp = 0;


	if(rtShadow) {
		delete rtShadow;
	}
	rtShadow = new RenderTexture(2048, 2048, GL_TEXTURE_2D);
	rtShadow->InitDepth_Tex();
	//no color buffer in this case, we only need depth
	rtShadow->Activate();
	glDrawBuffer(GL_NONE);
	glReadBuffer(GL_NONE);
	rtShadow->Deactivate();

	if (rtIrradiance) {
		delete rtIrradiance;
	}
	rtIrradiance = new RenderTexture(win_w,win_h, GL_TEXTURE_2D);
	rtIrradiance->InitColor_Tex(0, GL_RGBA32F_ARB);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	rtIrradiance->InitDepth_RB();

	if (rtPosition) {
		delete rtPosition;
	}
	rtPosition = new RenderTexture(win_w,win_h, GL_TEXTURE_2D);
	rtPosition->InitColor_Tex(0, GL_RGBA32F_ARB);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	rtPosition->InitDepth_RB();

	for(int i=0; i<6; i++){
		if (rtGaussianPass[i]) {
			delete rtGaussianPass[i];
		}
		rtGaussianPass[i] = new RenderTexture(win_w,win_h, GL_TEXTURE_2D);
		rtGaussianPass[i]->InitColor_Tex(0, GL_RGBA32F_ARB);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		// no depth buffer
		//rtGaussianPass[i]->InitDepth_RB();
	}

	if (rtTemp) {
		delete rtTemp;
	}
	rtTemp = new RenderTexture(win_w, win_h, GL_TEXTURE_2D);
	rtTemp->InitColor_Tex(0, GL_RGBA32F_ARB);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	// rtTemp->InitDepth_RB();





}
void init(void)
{

	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_COLOR_MATERIAL);
	glShadeModel(GL_SMOOTH);
	glEnable(GL_DEPTH_TEST);
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);


	cgSetErrorCallback(CheckCgError);

	L = vec3f(0,0,50);
	V = vec3f(0,0,50);

	for(int i=0; i<3; i++){
		R_trackball[i] = R_trackball[i].Identity();
		R_temp[i] = R_temp[i].Identity();
	}


	w_image = 1300;
	h_image = 1954;
	fc = 15260.800000;
	pu = 656.011000; 
	pv = 851.036000;
	//T = vec3f(-28.386800, -6.662780, 174.700000);
	//Rx = vec3f(0.987674, -0.030136, -0.153596);
	//Ry = vec3f(0.026308, 0.999292, -0.026899);
	//Rz = vec3f(0.154298, 0.022527, 0.987768);

	T = vec3f(0, 0, 0);
	Rx = vec3f(0, 0, 0);
	Ry = vec3f(0, 0, 0);
	Rz = vec3f(0, 0, 0);


	setDefaultCamera();

	createBuffers();



}


void init_shaders(void)
{

	// pass the pointer of each shader parameter into shader handler
	// each time shader is enabled, the shader will use the pointers to get the value of each parameter

	irradianceFShader.m_parameters.clear();
	irradianceFShader.AddParameter("albedo", CG_GL_TEXTURE_2D, &cgTextureIDAlbedo);
	irradianceFShader.AddParameter("normalR", CG_GL_TEXTURE_2D, &cgTextureIDNormalR);
	irradianceFShader.AddParameter("normalG", CG_GL_TEXTURE_2D, &cgTextureIDNormalG);
	irradianceFShader.AddParameter("normalB", CG_GL_TEXTURE_2D, &cgTextureIDNormalB);
	irradianceFShader.AddParameter("env",			CG_GL_TEXTURE_CUBE_MAP, &cgEnvID);
	irradianceFShader.AddParameter("envDiff",		CG_GL_TEXTURE_CUBE_MAP, &cgEnvDiffID);
	irradianceFShader.AddParameter("envSpec1",	CG_GL_TEXTURE_CUBE_MAP, &cgEnvSpec1ID);
	irradianceFShader.AddParameter("envSpec2",	CG_GL_TEXTURE_CUBE_MAP, &cgEnvSpec2ID);

	irradianceFShader.AddParameter("L",		CG_GL_VECTOR_4D, &L);
	irradianceFShader.AddParameter("V",		CG_GL_VECTOR_4D, &V);
	irradianceFShader.AddParameter("objectToLightMatrix", CG_GL_ARBITRARY_MTX, ObjectToLightMatrix);
	irradianceFShader.AddParameter("projectiveMatrix", CG_GL_ARBITRARY_MTX, ProjectiveMatrix);
	irradianceFShader.AddParameter("shadowMap", CG_GL_TEXTURE_2D, &cgTextureIDShadowMap);

	ublurFShader.m_parameters.clear();
	ublurFShader.AddParameter("irradianceTexture",	CG_GL_TEXTURE_2D, &cgTextureIDIrradiance);
	ublurFShader.AddParameter("positionTexture",	CG_GL_TEXTURE_2D, &cgTextureIDPosition);
	ublurFShader.AddParameter("gaussWidth",	CG_GL_VECTOR_1D, &cgFloatGaussianWidth);

	vblurFShader.m_parameters.clear();
	vblurFShader.AddParameter("irradianceTexture",	CG_GL_TEXTURE_2D, &cgTextureIDIrradiance);
	vblurFShader.AddParameter("positionTexture",	CG_GL_TEXTURE_2D, &cgTextureIDPosition);
	vblurFShader.AddParameter("gaussWidth",	CG_GL_VECTOR_1D, &cgFloatGaussianWidth);

	gaussianSkinFShader.m_parameters.clear();
	gaussianSkinFShader.AddParameter("irradianceTexture",	CG_GL_TEXTURE_2D, &cgTextureIDIrradiance);
	gaussianSkinFShader.AddParameter("positionTexture",		CG_GL_TEXTURE_2D, &cgTextureIDPosition);
	gaussianSkinFShader.AddParameter("gaussianTexture1",	CG_GL_TEXTURE_2D, &cgTextureIDGaussian[0]);
	gaussianSkinFShader.AddParameter("gaussianTexture2",	CG_GL_TEXTURE_2D, &cgTextureIDGaussian[1]);
	gaussianSkinFShader.AddParameter("gaussianTexture3",	CG_GL_TEXTURE_2D, &cgTextureIDGaussian[2]);
	gaussianSkinFShader.AddParameter("gaussianTexture4",	CG_GL_TEXTURE_2D, &cgTextureIDGaussian[3]);
	gaussianSkinFShader.AddParameter("gaussianTexture5",	CG_GL_TEXTURE_2D, &cgTextureIDGaussian[4]);
	gaussianSkinFShader.AddParameter("gaussianTexture6",	CG_GL_TEXTURE_2D, &cgTextureIDGaussian[5]);
	gaussianSkinFShader.AddParameter("albedo",				CG_GL_TEXTURE_2D, &cgTextureIDAlbedo);
	gaussianSkinFShader.AddParameter("normalR",				CG_GL_TEXTURE_2D, &cgTextureIDNormalR);
	gaussianSkinFShader.AddParameter("normalG",				CG_GL_TEXTURE_2D, &cgTextureIDNormalG);
	gaussianSkinFShader.AddParameter("normalB",				CG_GL_TEXTURE_2D, &cgTextureIDNormalB);
	gaussianSkinFShader.AddParameter("env",					CG_GL_TEXTURE_CUBE_MAP, &cgEnvID);
	gaussianSkinFShader.AddParameter("envDiff",				CG_GL_TEXTURE_CUBE_MAP, &cgEnvDiffID);
	gaussianSkinFShader.AddParameter("envSpec1",			CG_GL_TEXTURE_CUBE_MAP, &cgEnvSpec1ID);
	gaussianSkinFShader.AddParameter("envSpec2",			CG_GL_TEXTURE_CUBE_MAP, &cgEnvSpec2ID);
	gaussianSkinFShader.AddParameter("L",		CG_GL_VECTOR_4D, &L);
	gaussianSkinFShader.AddParameter("V",		CG_GL_VECTOR_4D, &V);
	gaussianSkinFShader.AddParameter("objectToLightMatrix", CG_GL_ARBITRARY_MTX, ObjectToLightMatrix);
	gaussianSkinFShader.AddParameter("projectiveMatrix", CG_GL_ARBITRARY_MTX, ProjectiveMatrix);
	gaussianSkinFShader.AddParameter("shadowMap", CG_GL_TEXTURE_2D, &cgTextureIDShadowMap);

	compositeFShader.m_parameters.clear();
	compositeFShader.AddParameter("irradianceTexture",	CG_GL_TEXTURE_2D, &cgTextureIDIrradiance);
	compositeFShader.AddParameter("positionTexture",	CG_GL_TEXTURE_2D, &cgTextureIDPosition);

	aoFShader.m_parameters.clear();
	aoFShader.AddParameter("positionTexture",	CG_GL_TEXTURE_2D, &cgTextureIDPosition);

}





void funcMouse(int button, int button_state, int x, int y )
{


	/**********************************
	mouseMotion
	0: camera rotation			(mouse_left)
	1: camera translation xy	(mouse_middle)
	2: light rotation			(mouse_right) & (mouse_left + ALT)
	3: camera translation z		(mouse_middle + mouse_right)
	4: object rotation			(mouse_left + CTRL)
	**********************************/

	int mod = glutGetModifiers();

	if(button == 0 && button_state == 0){//mouse_left
		if(mod == GLUT_ACTIVE_ALT)
			mouseMotion = 2;
		else if(mod == GLUT_ACTIVE_CTRL)
			mouseMotion = 4;
		else if(mod == 0)
			mouseMotion = 0;
	}else if(button == 1 && button_state == 0){//mouse_middle
		if(mouseMotion == 2)
			mouseMotion = 3;
		else
			mouseMotion = 1;
	}else if(button == 2 && button_state == 0){//mouse_right
		if(mouseMotion == 1)
			mouseMotion = 3;
		else
			mouseMotion = 2;
	}else if(button == 1 && button_state == 1){//mouse_middle release
		if(mouseMotion == 3)
			mouseMotion = 2;
		else
			mouseMotion = -1;
	}else if(button == 2 && button_state == 1){//mouse_middle release
		if(mouseMotion == 3)
			mouseMotion = 1;
		else
			mouseMotion = -1;
	}else if(button_state == 1){//mouse release
		mouseMotion = -1;

	}


	//printf("button %d button_state %d mod %d mouseMotion %d\n", button, button_state, mod, mouseMotion);

	mouseBeginX=x;
	mouseBeginY=y;


	switch(mouseMotion){
case 0:
	trackballs[0].updateV1(x, y, win_w, win_h);
	break;
case 2:
	// for light
	// only rotate horizontally
	if(isNoVerticalLightMovement)
		trackballs[1].updateV1(x, win_h/2, win_w, win_h);
	else
		trackballs[1].updateV1(x, y, win_w, win_h);
	break;
case 4:
	trackballs[2].updateV1(x, y, win_w, win_h);
	break;
default:
	break;

	}


}

void funcMotion(int x, int y )
{


	if(mouseMotion == 1){
		x_camera += (x - mouseBeginX)/20.0;
		y_camera -= (y - mouseBeginY)/20.0;
		printf("%f %f\n", (x - mouseBeginX)/20.0, -(y - mouseBeginY)/20.0);
		mouseBeginX = x;
		mouseBeginY = y;

	}else if(mouseMotion == 3){
		z_camera += (y - mouseBeginY)/5.0;
		printf("%f\n", (y - mouseBeginY)/5.0);
		mouseBeginX = x;
		mouseBeginY = y;

	}else if(mouseMotion == 0){
		target = 0;
		R_trackball[0] = trackballs[0].R;
		trackballs[0].updateV2(x, y, win_w, win_h);
		R_temp[0] = trackballs[0].R_temp;

		trackballs[0].R = trackballs[0].R_temp * trackballs[0].R;

	}else if(mouseMotion == 2){
		target = 1;
		R_trackball[1] = trackballs[1].R;
		// for light
		// only rotate horizontally
		if(isNoVerticalLightMovement)
			trackballs[1].updateV2(x, win_h/2, win_w, win_h);
		else
			trackballs[1].updateV2(x, y, win_w, win_h);
		R_temp[1] = trackballs[1].R_temp;

		trackballs[1].R =  trackballs[1].R_temp * trackballs[1].R;

	}else if(mouseMotion == 4){
		target = 2;
		R_trackball[2] = trackballs[2].R;
		trackballs[2].updateV2(x, y, win_w, win_h);
		R_temp[2] = trackballs[2].R_temp;

		trackballs[2].R = trackballs[2].R_temp * trackballs[2].R;
	}
	//printf("%f %f %f", (x - mouseBeginX)/20.0, -(y - mouseBeginY)/20.0);

	glutPostRedisplay(); 
}

void funcReshape(int x, int y)
{
	// win_x and win_h is the size of the whole window (opengl area + UI area)
	win_w=x; win_h=y;

	glViewport(0, 0, win_w, win_h);
	m_aspect = (float)(win_w)/(float)win_h; 

	createBuffers();
}

float* convertToOpenGLMatrix(mat4f M)
{
	float* array = (float*)malloc(sizeof(float)*16);
	M = M.Transpose();
	for(int i=0; i<4; i++)
		for(int j=0; j<4; j++)
			array[i*4+j] = M[i][j];
	return array;
}
mat4f convertFromOpenGLMatrix(float* m)
{
	mat4f M;
	for(int i=0; i<4; i++)
		for(int j=0; j<4; j++)
			M[i][j] = m[i*4+j];
	M = M.Transpose();
	return M;
}




mat4f M_wc;
mat4f M_lw;
mat4f M_ow;
mat4f M_lc;
mat4f M_oc;
mat4f M_ol;
mat4f M_projection_l;
mat4f M_projective_l;
mat4f M_projection_c;
mat4f M_projective_c;

void updateMatrix()
{
	/*
	[transformation matrix]
	M_wc 
	= world to camera 
	= how world moves according to camera 
	= world space transform to camera space

	[multiplication of matrix]
	M_lc
	= light to camera
	= light to world * world to camera
	= M_wc * M_lw [the order is important]
	*/
	//known:
	//world to camera	M_wc	
	//light to world	M_lw
	//object to world	M_ow
	//[drawing in camera space]
	//world to camera	M_wc
	//light to camera	M_lc
	//object to camera	M_oc
	//[drawing in light space]
	//object to light	M_ol
	//[projective matrix from light]
	//projectiveMatrix=biasMatrix*light projection matrix*object to light matrix
	mat3f R_wc = trackballs[0].R;
	vec3f T_wc = vec3f(pos.x+x_camera, pos.y+y_camera, pos.z+z_camera);
	M_wc = mat4f(R_wc, T_wc);

	mat3f R_lw = trackballs[1].R;
	vec3f T_lw = R_lw * vec3f(0,0,50);//translate along new axis
	M_lw = mat4f(R_lw, T_lw);

	mat3f R_ow = trackballs[2].R;
	M_ow = mat4f(R_ow, vec3f(0,0,0));

	M_lc = M_wc*M_lw;
	M_oc = M_wc*M_ow;

	M_ol = M_lw.Inverse()*M_ow;

	mat4f M_bias(0.5f, 0.0f, 0.0f, 0.0f,
		0.0f, 0.5f, 0.0f, 0.0f,
		0.0f, 0.0f, 0.5f, 0.0f,
		0.5f, 0.5f, 0.5f, 1.0f);
	M_bias = M_bias.Transpose();

	GLfloat m[16];
	//[fixme]the one comes first is co
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	float nearPlane = -z_camera-30; if(nearPlane<0.1) nearPlane = 0.1;
	float farPlane = -z_camera+30;
	nearPlane = 1;
	farPlane = 1000.0;
	//gluPerspective(m_frustum_y,m_aspect, nearPlane, farPlane);
	gluPerspective(20,m_aspect, nearPlane, farPlane);
	glGetFloatv(GL_PROJECTION_MATRIX, m);
	M_projection_c = convertFromOpenGLMatrix(m);


	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(30,m_aspect, 30, 70);
	glGetFloatv(GL_PROJECTION_MATRIX, m);
	M_projection_l = convertFromOpenGLMatrix(m);

	//progective matrix from light
	M_projective_l = M_bias * M_projection_l * M_ol;
	//projective matrix from camera
	//M_projective_c = M_bias * M_projection_c * M_oc;


}
void printScreen(int W, int H, char* filename){
	// read from buffer
	//unsigned char *buf = new unsigned char[4*W*H];	
	float* buf = new float[4*W*H];	
	glReadBuffer(GL_BACK);
	glReadPixels(0,0,W, H,GL_RGBA,GL_FLOAT,buf);
	char str[100];

	// write to image
	floatimage img;
	img.Allocate(W,H);
	int i= 0;
	for(int y=H-1; y>=0; y--)
		for(int x=0; x<W; x++){

			img.line(y)[x].r = float(buf[i++]);
			img.line(y)[x].g = float(buf[i++]);
			img.line(y)[x].b = float(buf[i++]);
			i++;

		}
		/*
		for(int i=0; i<img.sizex; i++)
		for(int j=0; j<img.sizey; j++){
		img.line(j)[i].r = powf(img.line(j)[i].r, 2.2);
		img.line(j)[i].g = powf(img.line(j)[i].g, 2.2);
		img.line(j)[i].b = powf(img.line(j)[i].b, 2.2);

		}
		*/
		//sprintf(str, ".bmp", );
		img.SavePFM(filename);

		delete [] buf;



}
void drawQuad()
{
	// w, h is the window for open gl, which is the subset of win_h, win_w
	//int tx, ty, tw, th;
	//GLUI_Master.get_viewport_area(&tx, &ty, &tw, &th);
	float w = win_w;
	float h = win_h;
	//printf("!!%d %d %d %d\n", tx, ty, tw, th);	
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(-w/2.0, w/2.0, -h/2.0, h/2.0);
	glViewport(0, 0, win_w, win_h);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	int x = -w/2.0;
	int y = h/2.0;
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	glPushMatrix();
	glTranslatef(x,y,0);
	glEnable(GL_TEXTURE_2D);
	glDisable(GL_LIGHTING);
	//[FIXME] +0.5 then it will aligned well
	float a = float(win_h-h)/float(win_h);
	glBegin(GL_QUADS);									// Start Drawing A Textured Quad
	glTexCoord2f(0.0f, a); glVertex3f(0,-h+0.5,0);	// Bottom Left
	glTexCoord2f(1.0f, a); glVertex3f(w,-h+0.5,0);	// Bottom Right
	glTexCoord2f(1.0f, 1.0f); glVertex3f(w,+0.5,0);	// Top Right
	glTexCoord2f(0.0f, 1.0f); glVertex3f(0,+0.5,0);	// Top Left
	printf("%f %f %f %d %d\n", a, w, h, x, y);
	glEnd();			
	glEnable(GL_LIGHTING);
	glDisable(GL_TEXTURE_2D);
	glPopMatrix();
}
void draw()
{

	// Pass1: draw the depth of model from light in light space onto shadow render texture 
	// Pass2: draw the irradiance of model from camera in UV space onto irradiance render texture 
	// Pass3,4,5,6,7,8: apply gaussian blur with 6 different kernels
	// 



			static int gank=0;

			if (gank==0)
			{
				//ifPrintScreen=true;
			}

			updateMatrix();

			float *cache;

			float mpl[16];
			float mol[16];

			cache=convertToOpenGLMatrix(M_projection_l);

			for (int i=0;i<16;i++)
			{
				mpl[i]=cache[i];
			}

			cache=convertToOpenGLMatrix(M_ol);

			for (int i=0;i<16;i++)
			{
				mol[i]=cache[i];
			}

			rtShadow->Activate();

			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			glViewport(0,0,rtShadow->GetWidth(), rtShadow->GetHeight());


			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			//light projection
			glLoadMatrixf( mpl );

			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
			//object to light
			glLoadMatrixf( mol );

			//glScalef(1.05,1.05,1.05);
			drawModel();


			rtShadow->Deactivate();
			rtIrradiance->Activate();

			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			glViewport(0,0,rtIrradiance->GetWidth(), rtIrradiance->GetHeight());

			//drawSky();

			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			//camera projection
			glLoadMatrixf( convertToOpenGLMatrix(M_projection_c) );



			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();

			//world to camera -> how world moves according to camera (M_wc)
			glLoadMatrixf( convertToOpenGLMatrix(M_wc) );
			//drawAxes(10);

			// object to camera -> how object moves according to camera (M_oc)
			glLoadMatrixf( convertToOpenGLMatrix(M_oc) );

			//shader parameters for object
			//1. light position 2. camera position 3. objectToEnvironmentMatrix 4. projectiveMatrix
			vec4f L_o = M_ol.Inverse() * vec4f(0,0,0,1);
			vec4f V_o = M_oc.Inverse() * vec4f(0,0,0,1);
			L = vec3f(L_o.x, L_o.y, L_o.z);
			V = vec3f(V_o.x, V_o.y, V_o.z);
			mat4f R = M_ol.Transpose();//OpenGL is column-major
			for(int i=0; i<4; i++)
				for(int j=0; j<4; j++){
					ObjectToLightMatrix[i*4+j] = R[i][j];
				}
				R = M_projective_l.Transpose();
				for(int i=0; i<4; i++)
					for(int j=0; j<4; j++){
						ProjectiveMatrix[i*4+j] = R[i][j];
					}
					cgTextureIDShadowMap = rtShadow->GetDepthTex();

					//draw object in object space
					irradianceVShader.bind();
					irradianceFShader.bind();
					drawModel();
					irradianceVShader.release();
					irradianceFShader.release();

					if(ifPrintScreen)
						printScreen(win_w, win_h, "irradiance.pfm");


					rtIrradiance->Deactivate();

			rtPosition->Activate();

			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			glViewport(0,0,rtPosition->GetWidth(), rtPosition->GetHeight());


			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			//camera projection
			glLoadMatrixf( convertToOpenGLMatrix(M_projection_c) );

			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
			// object to camera -> how object moves according to camera (M_oc)
			glLoadMatrixf( convertToOpenGLMatrix(M_oc) );

			//no shader parameters for object
			//draw object in object space
			irradianceVShader.bind();
			positionFShader.bind();
			drawModel();
			irradianceVShader.release();
			positionFShader.release();

			if(ifPrintScreen)
			printScreen(win_w, win_h, "strech.pfm");

			//drawImages();

			rtPosition->Deactivate();

			//6 gaussian kernels
			float gaussianVariance[6] = {0, 0.0484, 0.187, 0.567, 1.99, 7.41};
			float gaussianWidth[6];

			gaussianWidth[0] = 0;
			for(int i=1; i<6; i++)
			{
			// width = standard diviation
			// for each pass, we are going to blur the previous gaussian pass
			// so the variance equals to the variance - previous variance
			float v = gaussianVariance[i] - gaussianVariance[i-1];
			gaussianWidth[i] = sqrtf(v);
			printf("%f %f\n", v, gaussianWidth[i]);
			}


			for(int i=0; i<6; i++){

			rtTemp->Activate();
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			glViewport(0,0,rtTemp->GetWidth(), rtTemp->GetHeight());

			// shader parameters
			cgTextureIDPosition = rtPosition->GetColorTex();
			// for the frist blur pass, blur irradiance map
			// for the rest, blur the previous pass.
			if(i==0)
			cgTextureIDIrradiance = rtIrradiance->GetColorTex();
			else
			cgTextureIDIrradiance = rtGaussianPass[i-1]->GetColorTex();
			cgFloatGaussianWidth = gaussianWidth[i];

			gaussianSkinVShader.bind();
			ublurFShader.bind();
			drawQuad();
			gaussianSkinVShader.release();
			ublurFShader.release();	

			//char buf[256];
			//sprintf(buf, "%i_u.pfm", i);
			//if(ifPrintScreen)
			//	printScreen(win_w, win_h, buf);

			rtTemp->Deactivate();

			rtGaussianPass[i]->Activate();
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			glViewport(0,0,rtGaussianPass[i]->GetWidth(), rtGaussianPass[i]->GetHeight());
			// shader parameters
			cgTextureIDPosition = rtPosition->GetColorTex();
			cgTextureIDIrradiance = rtTemp->GetColorTex();
			cgFloatGaussianWidth = gaussianWidth[i];

			gaussianSkinVShader.bind();
			vblurFShader.bind();
			drawQuad();
			gaussianSkinVShader.release();
			vblurFShader.release();	

			//char buf[256];
			//sprintf(buf, "%i.pfm", i);
			//if(ifPrintScreen)
			//	printScreen(win_w, win_h, buf);

			rtGaussianPass[i]->Deactivate();

			}


			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			glViewport(0,0,rtIrradiance->GetWidth(), rtIrradiance->GetHeight());

			//drawSky();

			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			//camera projection
			glLoadMatrixf( convertToOpenGLMatrix(M_projection_c) );



			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();

			//world to camera -> how world moves according to camera (M_wc)
			glLoadMatrixf( convertToOpenGLMatrix(M_wc) );
			//drawAxes(10);

			// object to camera -> how object moves according to camera (M_oc)
			glLoadMatrixf( convertToOpenGLMatrix(M_oc) );

			//shader parameters for object
			//1. light position 2. camera position 3. objectToEnvironmentMatrix 4. projectiveMatrix
			//5. shadow map 6. 6 guassian filter pass

			for(int i=0; i<6; i++)
			cgTextureIDGaussian[i] = rtPosition->GetColorTex();


			//draw object in object space
			gaussianSkinVShader.bind();
			gaussianSkinFShader.bind();
			drawModel();
			gaussianSkinVShader.release();
			gaussianSkinFShader.release();

			


}

void draw_all()
{





	draw();
	glutSwapBuffers();
	return;



}
void funcDisplay(){





	draw_all();





}

void funcKeyboard(unsigned char Key, int x, int y)
{
	switch(Key)
	{
	case 'r':
		//g_scene->ReloadShaders();
		reloadShaders();
		init_shaders();

		break;
	case 'q':
		exit(0);
		break;
	case 'g':

		if(first_time){
			first_time = false;
			init_global_time = clock();

			//Sleep(int(1000.0/24.0));

			frame = 0.0;

			QueryPerformanceCounter(&init_time);
			//mp3->Play();
			pass_time = 0;
			QueryPerformanceCounter(&aTime);
		}

		if(!play) {
			play = true; 
			if(!first_time){

				//mp3->Resume();
				QueryPerformanceCounter(&bTime);
				LARGE_INTEGER  freq;
				QueryPerformanceFrequency(&freq);
				pass_time += float(bTime.QuadPart - aTime.QuadPart)/float(freq.QuadPart);
				printf("pass time: %f\n", pass_time);
			}
		}else {
			play = false; 
			//mp3->Pause();
			QueryPerformanceCounter(&aTime);

			//isSetDefaultCamera = true;
			//frame = 0.0;
			//mp3->Stop();
			first_time = true;

		}
		//if(first_time) first_time = false;
		break;
	case 'l':
		if(isDrawLine == 0)
			isDrawLine = 1;
		else
			isDrawLine = 0;
		break;
	case 'p':
		ifPrintScreen = true;
		break;

	default:
		break;
	};

	glutPostRedisplay();
}

void initUI()
{

	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
	glutInitWindowPosition( 0, 0 );

	glutInitWindowSize(win_w, win_h);
	g_window_id = glutCreateWindow("Real-Time Facial Performance Demo");


	glutDisplayFunc(funcDisplay);
	glutReshapeFunc(funcReshape);
	glutKeyboardFunc(funcKeyboard);
	glutMouseFunc(funcMouse);
	glutMotionFunc(funcMotion);

	glewInit();

}
void drawModel()
{

	int indexAlbedo = 0;
	int indexNormalR = 1;
	int indexNormalG = 2;
	int indexNormalB = 3;

	cgTextureIDAlbedo = textureID[indexAlbedo];
	cgTextureIDNormalR = textureID[indexNormalR];
	cgTextureIDNormalG = textureID[indexNormalG];
	cgTextureIDNormalB = textureID[indexNormalB];

	if(isDrawLine)
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	else
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);


	glColor3f(1,0,0);
	glCallList(displayListID[0]);

}


void reloadShaders()
{
	// Load Shader
	//CGcontext m_context = cgCreateContext();
	//ShaderHandler vShader, fShader;
	irradianceVShader.m_context = context;
	irradianceVShader.Load("./Data/shader/vIrradiance.cg", CG_GL_VERTEX);
	if(irradianceVShader.m_program == NULL)
	{
		CGerror Error = cgGetError();
		MessageBox(NULL, cgGetErrorString(Error), "Error", MB_OK);													
	}

	irradianceFShader.m_context = context;
	irradianceFShader.Load("./Data/shader/fIrradiance.cg", CG_GL_FRAGMENT);
	if(irradianceFShader.m_program == NULL)
	{
		CGerror Error = cgGetError();
		MessageBox(NULL, cgGetErrorString(Error), "Error", MB_OK);													
	}

	positionVShader.m_context = context;
	positionVShader.Load("./Data/shader/vPosition.cg", CG_GL_VERTEX);
	if(positionVShader.m_program == NULL)
	{
		CGerror Error = cgGetError();
		MessageBox(NULL, cgGetErrorString(Error), "Error", MB_OK);													
	}

	positionFShader.m_context = context;
	positionFShader.Load("./Data/shader/fPosition.cg", CG_GL_FRAGMENT);
	if(positionFShader.m_program == NULL)
	{
		CGerror Error = cgGetError();
		MessageBox(NULL, cgGetErrorString(Error), "Error", MB_OK);													
	}

	vblurFShader.m_context = context;
	vblurFShader.Load("./Data/shader/fVBlur.cg", CG_GL_FRAGMENT);
	if(vblurFShader.m_program == NULL)
	{
		CGerror Error = cgGetError();
		MessageBox(NULL, cgGetErrorString(Error), "Error", MB_OK);													
	}

	ublurFShader.m_context = context;
	ublurFShader.Load("./Data/shader/fUBlur.cg", CG_GL_FRAGMENT);
	if(ublurFShader.m_program == NULL)
	{
		CGerror Error = cgGetError();
		MessageBox(NULL, cgGetErrorString(Error), "Error", MB_OK);													
	}

	gaussianSkinVShader.m_context = context;
	gaussianSkinVShader.Load("./Data/shader/vGaussianSkin.cg", CG_GL_VERTEX);
	if(gaussianSkinVShader.m_program == NULL)
	{
		CGerror Error = cgGetError();
		MessageBox(NULL, cgGetErrorString(Error), "Error", MB_OK);													
	}

	gaussianSkinFShader.m_context = context;
	gaussianSkinFShader.Load("./Data/shader/fGaussianSkin.cg", CG_GL_FRAGMENT);
	if(gaussianSkinFShader.m_program == NULL)
	{
		CGerror Error = cgGetError();
		MessageBox(NULL, cgGetErrorString(Error), "Error", MB_OK);													
	}
}
void initModel()
{
	// Load geometry
	// Make display lists
	// Load textures


	// Load geometry and make display lists
	totalFrame = 1;
	displayListID = (int*)malloc(totalFrame*sizeof(int));

	OBJHandler mesh;
	mesh.LoadOBJascii("./Data/sLang/lowres.obj");

	displayListID[0] = glGenLists(1);
	glNewList(displayListID[0], GL_COMPILE);
	glBegin(GL_TRIANGLES);
	for(int j=0; j<mesh.numTris; j++){
		int indexV1 = mesh.tris[j].v1;
		int indexV2 = mesh.tris[j].v2;
		int indexV3 = mesh.tris[j].v3;
		int indexUV1 = mesh.tris[j].uv1;
		int indexUV2 = mesh.tris[j].uv2;
		int indexUV3 = mesh.tris[j].uv3;
		glTexCoord2f(mesh.uvs[indexUV1].u, mesh.uvs[indexUV1].v); 
		glNormal3f(mesh.normal[indexV1].x, mesh.normal[indexV1].y, mesh.normal[indexV1].z);
		glColor3f(mesh.tangent[indexV1].x, mesh.tangent[indexV1].y, mesh.tangent[indexV1].z);
		glVertex3f(mesh.verts[indexV1].x, mesh.verts[indexV1].y, mesh.verts[indexV1].z);

		glTexCoord2f(mesh.uvs[indexUV2].u, mesh.uvs[indexUV2].v); 
		glNormal3f(mesh.normal[indexV2].x, mesh.normal[indexV2].y, mesh.normal[indexV2].z);
		glColor3f(mesh.tangent[indexV2].x, mesh.tangent[indexV2].y, mesh.tangent[indexV2].z);
		glVertex3f(mesh.verts[indexV2].x, mesh.verts[indexV2].y, mesh.verts[indexV2].z);

		glTexCoord2f(mesh.uvs[indexUV3].u, mesh.uvs[indexUV3].v); 
		glNormal3f(mesh.normal[indexV3].x, mesh.normal[indexV3].y, mesh.normal[indexV3].z);
		glColor3f(mesh.tangent[indexV3].x, mesh.tangent[indexV3].y, mesh.tangent[indexV3].z);
		glVertex3f(mesh.verts[indexV3].x, mesh.verts[indexV3].y, mesh.verts[indexV3].z);
	}
	glEnd();
	glEndList();

	// Load texture
	textureID = (int*)malloc(4*totalFrame*sizeof(int));

	// Load albedo
	TextureHandler texture1; 
	texture1.Load("./Data/sLang/diff.bmp");
	textureID[0] = texture1.ogl_id;
	// Load normal r
	TextureHandler texture2; 
	texture2.Load("./Data/sLang/diff_normal_r.bmp");
	textureID[1] = texture2.ogl_id;
	// Load normal g
	TextureHandler texture3; 
	texture3.Load("./Data/sLang/diff_normal_b.bmp");
	textureID[2] = texture3.ogl_id;
	// Load normal b
	TextureHandler texture4; 
	texture4.Load("./Data/sLang/spec_normal.bmp");
	textureID[3] = texture4.ogl_id;




}

int main(int argc, char* argv[])
{

	initUI();

	init();

	initCg();

	initModel();

	reloadShaders();//reload shader files
	init_shaders();//reload shader params pointer

	glutMainLoop();
	return 0;
}